package com.sxt.permissionsdispatcher_processor.permissiondispatchercompiler;

import com.sxt.permissionannotation.PermissionDenied;
import com.sxt.permissionannotation.PermissionGrant;
import com.sxt.permissionannotation.PermissionRational;


import java.io.IOException;
import java.io.Writer;
import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;

public class RuntimePermissionAbstractProcessor extends AbstractProcessor {

    /**
     * 帮助操作类中元素的工具类对象
     */
    private Elements elementUtils;

    /**
     * 用于编译时日志的输出
     */
    private Messager messager;

    /**
     * 用来存放添加权限相关注解的类与相关方法的对应
     */
    private final HashMap<String, MethodInfo> methodInfoMap = new HashMap<>();

    private Filer filer;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);

        elementUtils = processingEnv.getElementUtils();
        messager = processingEnv.getMessager();
        filer = processingEnv.getFiler();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        methodInfoMap.clear();
        messager.printMessage(Diagnostic.Kind.NOTE, "快捷权限分发类生成流程");
        if (!handleAnnotationInfo(roundEnv, PermissionGrant.class)) {
            return false;
        }
        if (!handleAnnotationInfo(roundEnv, PermissionDenied.class)) {
            return false;
        }
        if (!handleAnnotationInfo(roundEnv, PermissionRational.class)) {
            return false;
        }

        for (String className : methodInfoMap.keySet()) {
            MethodInfo methodInfo = methodInfoMap.get(className);
            try {
                JavaFileObject sourceFile = filer.createSourceFile(methodInfo.getPackageName() + "." + methodInfo.getFileName());
                Writer writer = sourceFile.openWriter();
                writer.write(methodInfo.generateJavaCode());
                writer.flush();
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
                messager.printMessage(Diagnostic.Kind.NOTE, "写文件时出现异常: " + e.getMessage());
            }
        }

        return false;
    }

    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
    private boolean handleAnnotationInfo(RoundEnvironment roundEnv, Class<? extends Annotation> annotation) {
        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(annotation);
        for (Element element : elements) {
            if (!checkMethodValidator(element, annotation)) {
                return false;
            }
            // 通过检验的元素都是方法元素 也就是可执行的Element
            ExecutableElement methodElement = (ExecutableElement) element;
            // 方法的父节点是类 在此处对应TypeElement
            TypeElement enclosingElement = (TypeElement) methodElement.getEnclosingElement();
            String className = enclosingElement.getQualifiedName().toString();

            MethodInfo methodInfo = methodInfoMap.get(className);
            if (methodInfo == null) {
                methodInfo = new MethodInfo(elementUtils, enclosingElement);
                methodInfoMap.put(className, methodInfo);
            }
            Annotation methodElementAnnotation = methodElement.getAnnotation(annotation);
            String methodName = methodElement.getSimpleName().toString();
            List<? extends VariableElement> parameters = methodElement.getParameters();
            if (parameters == null || parameters.size() < 1) {
                throw new IllegalArgumentException(String.format("方法: %s 要使用 @%s 注解必须有一个[String[] permissions] 的独立参数", methodName, methodElementAnnotation.annotationType().getSimpleName()));
            }
            // 收集类中添加注解的方法、注解value信息
            if (methodElementAnnotation instanceof PermissionGrant) {
                int requestCode = ((PermissionGrant) methodElementAnnotation).value();
                methodInfo.getGrantMethodMap().put(requestCode, methodName);
            } else if (methodElementAnnotation instanceof PermissionDenied) {
                int requestCode = ((PermissionDenied) methodElementAnnotation).value();
                methodInfo.getDeniedMethodMap().put(requestCode, methodName);
            } else if (methodElementAnnotation instanceof PermissionRational) {
                int requestCode = ((PermissionRational) methodElementAnnotation).value();
                methodInfo.getRationalMethodMap().put(requestCode, methodName);
            }

        }
        return true;
    }

    private boolean checkMethodValidator(Element element, Class<? extends Annotation> annotation) {
        if (element.getKind() != ElementKind.METHOD) {
            return false;
        }
        // 只处理public修饰的方法，对于私有和抽象方法不予处理
        return !ClassValidator.isPrivate(element) && !ClassValidator.isAbstract(element);
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        HashSet<String> supportList = new HashSet<>();
        // 本工具只关注有以下与权限有关的注解标记的方法的类
        supportList.add(PermissionGrant.class.getCanonicalName());
        supportList.add(PermissionDenied.class.getCanonicalName());
        supportList.add(PermissionRational.class.getCanonicalName());
        return supportList;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }
}
